home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus Leser 19
/
Amiga Plus Leser CD 19.iso
/
Tools
/
Emulatoren
/
GameBoy
/
AmigaVGB
/
Z80.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-07-07
|
14KB
|
391 lines
/** VGB: portable GameBoy emulator ***************************/
/** **/
/** Z80.c **/
/** **/
/** This file contains implementation for the GameBoy CPU. **/
/** See Z80.h for the relevant definitions. Please, note **/
/** that this code can not be used to emulate a generic Z80 **/
/** because the GameBoy version of it differs from Z80 in **/
/** many ways. **/
/** **/
/** Copyright (C) Marat Fayzullin 1994,1995,1996 **/
/** You are not allowed to distribute this software **/
/** commercially. Please, notify me, if you make any **/
/** changes to this file. **/
/*************************************************************/
#include <stdio.h>
#include "GB.h"
#include "Tables.h"
/*** Registers ***********************************************/
/*** Z80 registers and running flag. ***/
/*************************************************************/
static reg R;
byte CPURunning;
/*** Interrupts **********************************************/
/*** Interrupt-related variables. ***/
/*************************************************************/
int IPeriod = 69905; /* Number of cycles between int. int-s */
byte IntSync = 1; /* 1 to generate internal interrupts */
byte IFlag = 0; /* If IFlag==1, gen. int. and set to 0 */
int ICount; /* Variable used to count CPU cycles */
/*** Trace and Trap ******************************************/
/*** Switches to turn tracing on and off in DEBUG mode. ***/
/*************************************************************/
#ifdef DEBUG
byte Trace=0; /* Tracing is on if Trace==1 */
word Trap=0xFFFF; /* When PC==Trap, set Trace=1 */
#endif
/*** TrapBadOps **********************************************/
/*** When 1, print warnings of illegal Z80 instructions. ***/
/*************************************************************/
byte TrapBadOps=0;
#define S(Fl) R.AF.B.l|=Fl
#define R(Fl) R.AF.B.l&=~(Fl)
#define FLAGS(Rg,Fl) R.AF.B.l=Fl|ZSTable[Rg];
#define M_RLC(Rg) \
R.AF.B.l=Rg>>7;Rg=(Rg<<1)|R.AF.B.l;R.AF.B.l|=ZSTable[Rg]
#define M_RRC(Rg) \
R.AF.B.l=Rg&0x01;Rg=(Rg>>1)|(R.AF.B.l<<7);R.AF.B.l|=ZSTable[Rg]
#define M_RL(Rg) \
if(Rg&0x80) \
{ \
Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \
R.AF.B.l=ZSTable[Rg]|C_FLAG; \
} \
else \
{ \
Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \
R.AF.B.l=ZSTable[Rg]; \
}
#define M_RR(Rg) \
if(Rg&0x01) \
{ \
Rg=(Rg>>1)|(R.AF.B.l<<7); \
R.AF.B.l=ZSTable[Rg]|C_FLAG; \
} \
else \
{ \
Rg=(Rg>>1)|(R.AF.B.l<<7); \
R.AF.B.l=ZSTable[Rg]; \
}
#define M_SLA(Rg) \
R.AF.B.l=Rg>>7;Rg<<=1;R.AF.B.l|=ZSTable[Rg]
#define M_SRA(Rg) \
R.AF.B.l=Rg&C_FLAG;Rg=(Rg>>1)|(Rg&0x80);R.AF.B.l|=ZSTable[Rg]
#define M_SLL(Rg) \
R.AF.B.l=Rg>>7;Rg=(Rg<<1)|0x01;R.AF.B.l|=ZSTable[Rg]
#define M_SRL(Rg) \
R.AF.B.l=Rg&0x01;Rg>>=1;R.AF.B.l|=ZSTable[Rg]
#define M_BIT(Bit,Rg) \
R.AF.B.l=(R.AF.B.l&~(N_FLAG|Z_FLAG))|H_FLAG|(Rg&(1<<Bit)? 0:Z_FLAG)
#define M_SET(Bit,Rg) Rg|=1<<Bit
#define M_RES(Bit,Rg) Rg&=~(1<<Bit)
#define M_POP(Rg) \
R.Rg.B.l=M_RDMEM(R.SP.W++);R.Rg.B.h=M_RDMEM(R.SP.W++);
#define M_PUSH(Rg) \
M_WRMEM(--R.SP.W,R.Rg.B.h);M_WRMEM(--R.SP.W,R.Rg.B.l)
#define M_CALL \
J.B.l=M_RDMEM(R.PC.W++);J.B.h=M_RDMEM(R.PC.W++); \
M_WRMEM(--R.SP.W,R.PC.B.h);M_WRMEM(--R.SP.W,R.PC.B.l); \
R.PC.W=J.W;ICount-=28
#define M_JP \
J.B.l=M_RDMEM(R.PC.W++);J.B.h=M_RDMEM(R.PC.W);R.PC.W=J.W;ICount-=4
#define M_JR \
R.PC.W+=(offset)M_RDMEM(R.PC.W)+1;ICount-=4
#define M_RET \
R.PC.B.l=M_RDMEM(R.SP.W++);R.PC.B.h=M_RDMEM(R.SP.W++);ICount-=8
#define M_RST(Ad) \
M_WRMEM(--R.SP.W,R.PC.B.h);M_WRMEM(--R.SP.W,R.PC.B.l);R.PC.W=Ad
#define M_LDWORD(Rg) \
R.Rg.B.l=M_RDMEM(R.PC.W++);R.Rg.B.h=M_RDMEM(R.PC.W++)
#define M_ADD(Rg) \
J.W=R.AF.B.h+Rg; \
R.AF.B.l=J.B.h|ZSTable[J.B.l]|((R.AF.B.h^Rg^J.B.l)&H_FLAG); \
R.AF.B.h=J.B.l
#define M_SUB(Rg) \
J.W=R.AF.B.h-Rg; \
R.AF.B.l=N_FLAG|-J.B.h|ZSTable[J.B.l]|((R.AF.B.h^Rg^J.B.l)&H_FLAG); \
R.AF.B.h=J.B.l
#define M_ADC(Rg) \
J.W=R.AF.B.h+Rg+(R.AF.B.l&C_FLAG); \
R.AF.B.l=J.B.h|ZSTable[J.B.l]|((R.AF.B.h^Rg^J.B.l)&H_FLAG); \
R.AF.B.h=J.B.l
#define M_SBC(Rg) \
J.W=R.AF.B.h-Rg-(R.AF.B.l&C_FLAG); \
R.AF.B.l=N_FLAG|-J.B.h|ZSTable[J.B.l]|((R.AF.B.h^Rg^J.B.l)&H_FLAG); \
R.AF.B.h=J.B.l
#define M_CP(Rg) \
J.W=R.AF.B.h-Rg; \
R.AF.B.l=N_FLAG|-J.B.h|ZSTable[J.B.l]|((R.AF.B.h^Rg^J.B.l)&H_FLAG)
#define M_AND(Rg) R.AF.B.h&=Rg;R.AF.B.l=H_FLAG|ZSTable[R.AF.B.h]
#define M_OR(Rg) R.AF.B.h|=Rg;R.AF.B.l=ZSTable[R.AF.B.h]
#define M_XOR(Rg) R.AF.B.h^=Rg;R.AF.B.l=ZSTable[R.AF.B.h]
#define M_IN(Rg) Rg=DoIn(R.BC.B.l);R.AF.B.l=(R.AF.B.l&C_FLAG)|ZSTable[Rg]
#define M_INC(Rg) \
Rg++; \
R.AF.B.l=(R.AF.B.l&C_FLAG)|ZSTable[Rg]|(Rg&0x0F? 0:H_FLAG)
#define M_DEC(Rg) \
Rg--; \
R.AF.B.l=N_FLAG|(R.AF.B.l&C_FLAG)|ZSTable[Rg]|((Rg&0x0F)==0x0F? H_FLAG:0)
#define M_ADDW(Rg1,Rg2) \
J.W=(R.Rg1.W+R.Rg2.W)&0xFFFF; \
R.AF.B.l= \
(R.AF.B.l&~(H_FLAG|N_FLAG|C_FLAG))| \
((R.Rg1.W^R.Rg2.W^J.W)&0x1000? H_FLAG:0)| \
(((long)R.Rg1.W+(long)R.Rg2.W)&0x10000? C_FLAG:0); \
R.Rg1.W=J.W
#define M_ADCW(Rg) \
I=R.AF.B.l&C_FLAG;J.W=(R.HL.W+R.Rg.W+I)&0xFFFF; \
R.AF.B.l= \
(((long)R.HL.W+(long)R.Rg.W+(long)I)&0x10000? C_FLAG:0)| \
(~(R.HL.W^R.Rg.W)&(R.Rg.W^J.W)&0x8000? V_FLAG:0)| \
((R.HL.W^R.Rg.W^J.W)&0x1000? H_FLAG:0)| \
(J.W? 0:Z_FLAG)|(J.B.h&S_FLAG); \
R.HL.W=J.W
#define M_SBCW(Rg) \
I=R.AF.B.l&C_FLAG;J.W=(R.HL.W-R.Rg.W-I)&0xFFFF; \
R.AF.B.l= \
N_FLAG| \
(((long)R.HL.W-(long)R.Rg.W-(long)I)&0x10000? C_FLAG:0)| \
((R.HL.W^R.Rg.W)&(R.HL.W^J.W)&0x8000? V_FLAG:0)| \
((R.HL.W^R.Rg.W^J.W)&0x1000? H_FLAG:0)| \
(J.W? 0:Z_FLAG)|(J.B.h&S_FLAG); \
R.HL.W=J.W
enum Codes
{
NOP,LD_BC_WORD,LD_xBC_A,INC_BC,INC_B,DEC_B,LD_B_BYTE,RLCA,
EX_AF_AF,ADD_HL_BC,LD_A_xBC,DEC_BC,INC_C,DEC_C,LD_C_BYTE,RRCA,
DJNZ,LD_DE_WORD,LD_xDE_A,INC_DE,INC_D,DEC_D,LD_D_BYTE,RLA,
JR,ADD_HL_DE,LD_A_xDE,DEC_DE,INC_E,DEC_E,LD_E_BYTE,RRA,
JR_NZ,LD_HL_WORD,LD_xWORD_HL,INC_HL,INC_H,DEC_H,LD_H_BYTE,DAA,
JR_Z,ADD_HL_HL,LD_HL_xWORD,DEC_HL,INC_L,DEC_L,LD_L_BYTE,CPL,
JR_NC,LD_SP_WORD,LD_xWORD_A,INC_SP,INC_xHL,DEC_xHL,LD_xHL_BYTE,SCF,
JR_C,ADD_HL_SP,LD_A_xWORD,DEC_SP,INC_A,DEC_A,LD_A_BYTE,CCF,
LD_B_B,LD_B_C,LD_B_D,LD_B_E,LD_B_H,LD_B_L,LD_B_xHL,LD_B_A,
LD_C_B,LD_C_C,LD_C_D,LD_C_E,LD_C_H,LD_C_L,LD_C_xHL,LD_C_A,
LD_D_B,LD_D_C,LD_D_D,LD_D_E,LD_D_H,LD_D_L,LD_D_xHL,LD_D_A,
LD_E_B,LD_E_C,LD_E_D,LD_E_E,LD_E_H,LD_E_L,LD_E_xHL,LD_E_A,
LD_H_B,LD_H_C,LD_H_D,LD_H_E,LD_H_H,LD_H_L,LD_H_xHL,LD_H_A,
LD_L_B,LD_L_C,LD_L_D,LD_L_E,LD_L_H,LD_L_L,LD_L_xHL,LD_L_A,
LD_xHL_B,LD_xHL_C,LD_xHL_D,LD_xHL_E,LD_xHL_H,LD_xHL_L,HALT,LD_xHL_A,
LD_A_B,LD_A_C,LD_A_D,LD_A_E,LD_A_H,LD_A_L,LD_A_xHL,LD_A_A,
ADD_B,ADD_C,ADD_D,ADD_E,ADD_H,ADD_L,ADD_xHL,ADD_A,
ADC_B,ADC_C,ADC_D,ADC_E,ADC_H,ADC_L,ADC_xHL,ADC_A,
SUB_B,SUB_C,SUB_D,SUB_E,SUB_H,SUB_L,SUB_xHL,SUB_A,
SBC_B,SBC_C,SBC_D,SBC_E,SBC_H,SBC_L,SBC_xHL,SBC_A,
AND_B,AND_C,AND_D,AND_E,AND_H,AND_L,AND_xHL,AND_A,
XOR_B,XOR_C,XOR_D,XOR_E,XOR_H,XOR_L,XOR_xHL,XOR_A,
OR_B,OR_C,OR_D,OR_E,OR_H,OR_L,OR_xHL,OR_A,
CP_B,CP_C,CP_D,CP_E,CP_H,CP_L,CP_xHL,CP_A,
RET_NZ,POP_BC,JP_NZ,JP,CALL_NZ,PUSH_BC,ADD_BYTE,RST00,
RET_Z,RET,JP_Z,PFX_CB,CALL_Z,CALL,ADC_BYTE,RST08,
RET_NC,POP_DE,JP_NC,OUTA,CALL_NC,PUSH_DE,SUB_BYTE,RST10,
RET_C,EXX,JP_C,INA,CALL_C,PFX_DD,SBC_BYTE,RST18,
RET_PO,POP_HL,JP_PO,EX_HL_xSP,CALL_PO,PUSH_HL,AND_BYTE,RST20,
RET_PE,LD_PC_HL,JP_PE,EX_DE_HL,CALL_PE,PFX_ED,XOR_BYTE,RST28,
RET_P,POP_AF,JP_P,DI,CALL_P,PUSH_AF,OR_BYTE,RST30,
RET_M,LD_SP_HL,JP_M,EI,CALL_M,PFX_FD,CP_BYTE,RST38
};
enum CodesCB
{
RLC_B,RLC_C,RLC_D,RLC_E,RLC_H,RLC_L,RLC_xHL,RLC_A,
RRC_B,RRC_C,RRC_D,RRC_E,RRC_H,RRC_L,RRC_xHL,RRC_A,
RL_B,RL_C,RL_D,RL_E,RL_H,RL_L,RL_xHL,RL_A,
RR_B,RR_C,RR_D,RR_E,RR_H,RR_L,RR_xHL,RR_A,
SLA_B,SLA_C,SLA_D,SLA_E,SLA_H,SLA_L,SLA_xHL,SLA_A,
SRA_B,SRA_C,SRA_D,SRA_E,SRA_H,SRA_L,SRA_xHL,SRA_A,
SLL_B,SLL_C,SLL_D,SLL_E,SLL_H,SLL_L,SLL_xHL,SLL_A,
SRL_B,SRL_C,SRL_D,SRL_E,SRL_H,SRL_L,SRL_xHL,SRL_A,
BIT0_B,BIT0_C,BIT0_D,BIT0_E,BIT0_H,BIT0_L,BIT0_xHL,BIT0_A,
BIT1_B,BIT1_C,BIT1_D,BIT1_E,BIT1_H,BIT1_L,BIT1_xHL,BIT1_A,
BIT2_B,BIT2_C,BIT2_D,BIT2_E,BIT2_H,BIT2_L,BIT2_xHL,BIT2_A,
BIT3_B,BIT3_C,BIT3_D,BIT3_E,BIT3_H,BIT3_L,BIT3_xHL,BIT3_A,
BIT4_B,BIT4_C,BIT4_D,BIT4_E,BIT4_H,BIT4_L,BIT4_xHL,BIT4_A,
BIT5_B,BIT5_C,BIT5_D,BIT5_E,BIT5_H,BIT5_L,BIT5_xHL,BIT5_A,
BIT6_B,BIT6_C,BIT6_D,BIT6_E,BIT6_H,BIT6_L,BIT6_xHL,BIT6_A,
BIT7_B,BIT7_C,BIT7_D,BIT7_E,BIT7_H,BIT7_L,BIT7_xHL,BIT7_A,
RES0_B,RES0_C,RES0_D,RES0_E,RES0_H,RES0_L,RES0_xHL,RES0_A,
RES1_B,RES1_C,RES1_D,RES1_E,RES1_H,RES1_L,RES1_xHL,RES1_A,
RES2_B,RES2_C,RES2_D,RES2_E,RES2_H,RES2_L,RES2_xHL,RES2_A,
RES3_B,RES3_C,RES3_D,RES3_E,RES3_H,RES3_L,RES3_xHL,RES3_A,
RES4_B,RES4_C,RES4_D,RES4_E,RES4_H,RES4_L,RES4_xHL,RES4_A,
RES5_B,RES5_C,RES5_D,RES5_E,RES5_H,RES5_L,RES5_xHL,RES5_A,
RES6_B,RES6_C,RES6_D,RES6_E,RES6_H,RES6_L,RES6_xHL,RES6_A,
RES7_B,RES7_C,RES7_D,RES7_E,RES7_H,RES7_L,RES7_xHL,RES7_A,
SET0_B,SET0_C,SET0_D,SET0_E,SET0_H,SET0_L,SET0_xHL,SET0_A,
SET1_B,SET1_C,SET1_D,SET1_E,SET1_H,SET1_L,SET1_xHL,SET1_A,
SET2_B,SET2_C,SET2_D,SET2_E,SET2_H,SET2_L,SET2_xHL,SET2_A,
SET3_B,SET3_C,SET3_D,SET3_E,SET3_H,SET3_L,SET3_xHL,SET3_A,
SET4_B,SET4_C,SET4_D,SET4_E,SET4_H,SET4_L,SET4_xHL,SET4_A,
SET5_B,SET5_C,SET5_D,SET5_E,SET5_H,SET5_L,SET5_xHL,SET5_A,
SET6_B,SET6_C,SET6_D,SET6_E,SET6_H,SET6_L,SET6_xHL,SET6_A,
SET7_B,SET7_C,SET7_D,SET7_E,SET7_H,SET7_L,SET7_xHL,SET7_A
};
void CodesCB(void)
{
static byte CyclesCB[256] =
{
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,
8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8
};
register byte I;
I=M_RDMEM(R.PC.W++);
ICount-=CyclesCB[I];
switch(I)
{
#include "CodesCB.h"
default:
ICount-=4;
if(TrapBadOps)
printf
(
"Unrecognized instruction: CB %X at PC=%hX\n",
M_RDMEM(R.PC.W-1),R.PC.W-2
);
}
}
/*** Reset Z80 registers: *********************************/
/*** This function can be used to reset the register ***/
/*** file before starting execution with Z80(). It sets ***/
/*** the registers to their initial values. ***/
/**********************************************************/
void ResetZ80(reg *Regs)
{
Regs->PC.W=0x0100;Regs->SP.W=0xF000;
Regs->AF.W=Regs->BC.W=Regs->DE.W=Regs->HL.W=0x0000;
Regs->I=0x00;Regs->IFF=0x00;
}
/*** Interpret Z80 code: **********************************/
/*** Registers have initial values from Regs. PC value ***/
/*** at which emulation stopped is returned by this ***/
/*** function. ***/
/**********************************************************/
word Z80(reg Regs)
{
static byte Cycles[256] =
{
4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,
4,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,
8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,
8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
8, 8, 8, 8, 8, 8, 4, 8, 4, 4, 4, 4, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,
8,12,12,12,12,16, 8,32, 8, 8,12, 0,12,12, 8,32,
8,12,12, 0,12,16, 8,32, 8, 8,12, 0,12, 0, 8,32,
12,12, 8, 0, 0,16, 8,32,16, 4,16, 0, 0, 0, 8,32,
12,12, 8, 4, 0,16, 8,32,12, 8,16, 4, 0, 0, 8,32
};
register byte I;
register pair J;
R=Regs;ICount=IPeriod;CPURunning=1;
for(;;)
{
#ifdef DEBUG
if(R.PC.W==Trap) Trace=1; /*** Turn tracing on if trapped ***/
if(Trace) Debug(&R); /*** Call single-step debugger ***/
#endif
I=M_RDMEM(R.PC.W++);
ICount-=Cycles[I];
switch(I)
{
#include "Codes.h"
case PFX_CB:
CodesCB();break;
case 0x10:
case HALT:
R.PC.W--;R.IFF|=0x80;break;
default:
ICount-=4;
if(TrapBadOps)
printf
(
"Unrecognized instruction: %X at PC=%hX\n",
M_RDMEM(R.PC.W-1),R.PC.W-1
);
}
if(ICount<=0)
{
ICount+=IPeriod;
if(!CPURunning) break;
if(IntSync) IFlag=1;
}
if(IFlag)
{
IFlag=0;J.W=Interrupt();
if((J.W!=0xFFFF)&&(R.IFF&0x81))
{
if(R.IFF&0x80) R.PC.W++;
IFLAGS&=~IMask;IMask=0x00;
R.IFF&=0x7E;
M_PUSH(PC);R.PC.W=J.W;
}
}
}
return(R.PC.W);
}